﻿/*
 *  Copyright (c) 2009 Alessandro Pilotti
 *
 *  Permission is hereby granted, free of charge, to any person
 *  obtaining a copy of this software and associated documentation
 *  files (the "Software"), to deal in the Software without
 *  restriction, including without limitation the rights to use,
 *  copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following
 *  conditions:
 *  
 *  The above copyright notice and this permission notice shall be
 *  included in all copies or substantial portions of the Software.
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *  OTHER DEALINGS IN THE SOFTWARE.
 */

using System;
using System.Collections;
using Gnome.Keyring;

namespace AlexPilotti.KeyStorageManager
{
    class GnomeKeyringProviderManager : IKeyStorageProviderManager
    {
        #region IKeyStorageProviderManager Members

        public bool IsAvailable
        {
            get
            {
                return Environment.OSVersion.Platform == PlatformID.Unix && Environment.GetEnvironmentVariable("GNOME_KEYRING_SOCKET") != null;
            }
        }

        public IKeyStorageProvider GetProviderInstance()
        {
            return new GnomeKeyringProvider();
        }

        #endregion
    }

    public sealed class GnomeKeyringProvider : IKeyStorageProvider
    {
        #region Private Data

        const string keyNameAttr = "GnomeKeyringProvider_Name";

        string keyring = null;

        #endregion

        #region Public Properties

        /// <summary>
        /// The GNOME keyring in use. Uses the default on if not set. Use "session" for a non persistent keyring.
        /// </summary>
        public string Keyring
        {
            get {  
                if (keyring == null)
                    keyring = Ring.GetDefaultKeyring();
                return keyring;
            }
            set 
            { 
                keyring = value; 
            }
        }

        #endregion

        #region Constructors

        internal GnomeKeyringProvider()
        {
        }

        #endregion

        #region Private Methods

        private Hashtable GetAttributes(string name)
        {
            Hashtable tbl = new Hashtable();
            //tbl["user"] = Environment.UserName;
            //tbl["domain"] = Environment.UserDomainName;            
            tbl[keyNameAttr] = name;

            return tbl;
        }

        private ItemData FindItem(string keyring, string name)
        {
            ItemData itemFound = null;
            try
            {
                Hashtable tbl = GetAttributes(name);
                foreach (ItemData item in Ring.Find(ItemType.GenericSecret, tbl))
                    if (item.Keyring == keyring)
                    {
                        itemFound = item;
                        break;
                    }
            }
            catch (KeyringException)
            {
                // Throws an exception when an item vannot be found
            }

            return itemFound;
        }

        #endregion

        #region IKeyStorageProvider Members

        public void StoreKey(string name, byte[] secret)
        {
            string encodedData = Convert.ToBase64String(secret);
            StoreKey(name, encodedData);
        }

        public void StoreKey(string name, string secret)
        {
            Hashtable tbl = GetAttributes(name);
            Ring.CreateItem(Keyring, ItemType.GenericSecret, name, tbl, secret, true);
        }

        public byte[] RetrieveKey(string name)
        {
            string secretStr = RetrieveKeyAsString(name);
            return secretStr != null ? Convert.FromBase64String(secretStr) : null;
        }

        public string RetrieveKeyAsString(string name)
        {
            ItemData item = FindItem(Keyring, name);
                
            return item != null ? item.Secret : null;
        }

        public bool DeleteKey(string name)
        {
            ItemData item = FindItem(Keyring, name);

            if(item != null)
                Ring.DeleteItem(Keyring, item.ItemID);

            return item != null;
        }

        #endregion
    }
}
